package com.practise.rabbitmqdemo.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 *死信队列
 * 死信队列其实也是一个正常的队列，可以被消费，只不过，它是专门用来存放一些被丢弃了的消息。
 * 因为如果消息直接被丢弃了其实是个很危险的事情，所以我们使用一个队列来存放这些消息，这个队列就被称为死信队列。
 * 通常什么消息会放置我们的死信队列呢？
 * 1.消息TTL过期
 * 2.队列达到了最大长度，无法再添加信息到MQ中
 * 3.消息被拒绝，并且没有重新入队
 *
 **/
@Configuration
public class DeadTTLDirectRabbitConfig {

    public static final String TTL_Q2 ="ttlQ2";
    public static final String TTL_EXCHANGE_2 ="ttlexchange2";
    public static final String TTL_ROUTKEY_2 ="ttl_2";

    public static final String DEAD_Q2 ="deadQ2";
    public static final String DEAD_EXCHANGE_2 ="deadexchange2";
    public static final String DEAD_ROUTKEY_2 ="dead_2";


    public static final String DEAD_Q3 ="deadQ3";
    public static final String DEAD_EXCHANGE_3 ="deadexchange3";
    public static final String DEAD_ROUTKEY_3 ="dead_3";


    @Bean
    public Queue getDeadQueue3() {
        return new Queue(DEAD_Q3,true,false,false);
    }
    @Bean
    CustomExchange getDeadExchange3() {
//        return new DirectExchange(DEAD_EXCHANGE_3,true,false);
//        插件方式实现死信队列
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange(DEAD_EXCHANGE_3, "x-delayed-message", true, false, args);
    }

    /**
     * TTL实现的死信队列 弊端就是如果使用在消息属性上设置TTL的方式，消息可能并不会按时“死亡“，因为RabbitMQ只会检查第一个消息是否过期，
     * 如果过期则丢到死信队列，所以如果第一个消息的延时时长很长，而第二个消息的延时时长很短，则第二个消息并不会优先得到执行，
     * 此处则不再进行编写代码，但是为了解决这个问题，我们将利用rabbitMq插件实现延迟队列
     * @return  插件方式实现死信队列
     */
    @Bean
    Binding bindingDeadDirect3() {
        return BindingBuilder.bind(getDeadQueue3()).to(getDeadExchange3()).with(DEAD_ROUTKEY_3).noargs();
    }


    @Bean
    public Queue getDeadQueue2() {
        return new Queue(DEAD_Q2,true,false,false);
    }
    @Bean
    DirectExchange getDeadExchange2() {
        return new DirectExchange(DEAD_EXCHANGE_2,true,false);
    }

    /**
     * @return  普通队列作为死信队列
     */
    @Bean
    Binding bindingDeadDirect2() {
        return BindingBuilder.bind(getDeadQueue2()).to(getDeadExchange2()).with(DEAD_ROUTKEY_2);
    }

    /**
     * 先创建一个普通队列作为死信队列，然后绑定到TTL过期队列 ttlQ2 ，即延迟队列
     * 示例场景：用户提交订单后，后台提交到TTL队列，如果用户超时未支付，TTL消息队列过期后将消息丢入死信队列，监听死信队列则回滚操作
     * @return
     */
    @Bean
    public Queue getTTLQueue2() {
        Map<String, Object> map = new HashMap<>();
        //基于队列最大长度实现死信队列，设置队列最大长度为5
//        map.put("x-max-length",5);

        //设置过期时间为30s
        map.put("x-message-ttl",30000);
        //下方设置死信队列的交换机名称，因为我是direct的交换机，所以还需要设置其routingKey，若是fanout模式则不需要设置routingKey
        map.put("x-dead-letter-exchange",DEAD_EXCHANGE_2);
        map.put("x-dead-letter-routing-key",DEAD_ROUTKEY_2);
        return new Queue(TTL_Q2,true,false,false,map);
    }

    @Bean
    DirectExchange getTTLExchange2() {
        return new DirectExchange(TTL_EXCHANGE_2,true,false);
    }

    @Bean
    Binding bindingTTLDirect2() {
        return BindingBuilder.bind(getTTLQueue2()).to(getTTLExchange2()).with(TTL_ROUTKEY_2);
    }


}